/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package controllers;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import jsc.independentsamples.MannWhitneyTest;
import jsc.tests.H1;
import org.apache.commons.math.random.RandomGenerator;
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
import org.uncommons.maths.random.MersenneTwisterRNG;
import regulatoryInteractionsStats.yeast.SemanticSimStats;
import units.CellType;
import units.MonkeyType;
import utils.FileUtils;
import utils.Handle;

/**
 *
 * @author jamie
 */
public class ControllerStatistics {

    private TreeMap<Integer, Double> bayes;
    private int iterations = 1000;
    private MersenneTwisterRNG twister;
    private int rewiresPerEdge = 1000;
    private Handle handle = Handle.getConnectedHandle("monkeys");

    public ControllerStatistics() {
        bayes = new SemanticSimStats().getBayesResults();
        twister = new MersenneTwisterRNG();
    }

    public Handle getHandle() {
        return handle;
    }



    public void run(MonkeyType mt, CellType ct) {
        File edgeFile = new File("/Users/jamie/Dropbox/monkeys/networks/regulatoryNetworks/" + mt.name() + ct.name() + "_edges.txt");
        List<Integer> hivInteractants = getHivInteractants(edgeFile);
        List<Integer> allProbeIds = getAllProbeIds(edgeFile);
        List<RegulatoryInteraction> regulatoryInteractions = getInteractionsFromFile(edgeFile);

        Subnetwork wholeNetwork = new Subnetwork();
        wholeNetwork.getProbeIds().addAll(allProbeIds);
        wholeNetwork.getRegulatoryInteractions().addAll(regulatoryInteractions);
        System.out.println(wholeNetwork.getRegulatoryInteractions().size() + ", " + wholeNetwork.getProbeIds().size() + " is size of whole network");

        HashMap<Integer, double[]> realSizeHash = new HashMap<Integer, double[]>();
        HashMap<Integer, double[]> randomSizeHash = new HashMap<Integer, double[]>();
        HashMap<Integer, SummaryStatistics> realSSHash = new HashMap<Integer, SummaryStatistics>();
        HashMap<Integer, SummaryStatistics> randomSSHash = new HashMap<Integer, SummaryStatistics>();

        for (int probeId : hivInteractants) {
            realSizeHash.put(probeId, new double[iterations]);
            randomSizeHash.put(probeId, new double[iterations]);
            realSSHash.put(probeId, new SummaryStatistics());
            randomSSHash.put(probeId, new SummaryStatistics());
        }
        

        for (int i = 0; i < iterations; i++) {

            System.out.println("iteration: " + i);
            Subnetwork rewiredWholeNetwork = rewire2(wholeNetwork);
            System.out.println(rewiredWholeNetwork.getRegulatoryInteractions().size() + ", " + rewiredWholeNetwork.getProbeIds().size() + " is size of rewired network");

            for (int probeId : hivInteractants) {
                Subnetwork sn_real = getPerturbednetwork(wholeNetwork.getRegulatoryInteractions(), probeId);
                Subnetwork sn_rewired = getPerturbednetwork(rewiredWholeNetwork.getRegulatoryInteractions(), probeId);
                realSizeHash.get(probeId)[i] = sn_real.probeIds.size();
                randomSizeHash.get(probeId)[i] = sn_rewired.probeIds.size();
                realSSHash.get(probeId).addValue(sn_real.probeIds.size());
                randomSSHash.get(probeId).addValue(sn_rewired.probeIds.size());
//                System.out.println(sn_real.probeIds.size() + "\t" + sn_rewired.probeIds.size());
            }

        }
        BufferedWriter writer = FileUtils.getBufferedWriter(new File("/Users/jamie/Dropbox/monkeys/controlStats/results_"+mt.name()+"_"+ct.name()+".txt"));
        for (int probeId : hivInteractants) {
            List<Integer> gIds = getGeneIds(probeId);
            MannWhitneyTest mwut = new MannWhitneyTest(realSizeHash.get(probeId), randomSizeHash.get(probeId), H1.GREATER_THAN);
//            System.out.println(probeId + "\t" + realSSHash.get(probeId).getMean() + "\t" + randomSSHash.get(probeId).getMean() + "\t" + mwut.getSP());
            try {
                writer.append(probeId + "\t");
                StringBuffer sb = new StringBuffer();
                for(int gId : gIds){
                    sb.append(gId+":"+getGeneSymbolFromId(gId)+" ");
                }
                writer.append(sb.toString().trim()+"\t"+ realSSHash.get(probeId).getMean() + "\t" + randomSSHash.get(probeId).getMean() + "\t" + mwut.getSP() + "\n");
            } catch (IOException ex) {
                Logger.getLogger(ControllerStatistics.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        try {
            writer.close();
        } catch (IOException ex) {
            Logger.getLogger(ControllerStatistics.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }

    private Subnetwork getPerturbednetwork(List<RegulatoryInteraction> allInteractions, int probeId) {
        return getSubnetwork(probeId, allInteractions, true);
    }

    private Subnetwork getSubnetwork(int probeId, List<RegulatoryInteraction> allInteractions, boolean perturb) {
        Subnetwork subNet = new Subnetwork();
        subNet.getProbeIds().add(probeId);
        List<RegulatoryInteraction> interactionsPool = getInteractionsPool(allInteractions, perturb);
        addToNetwork(subNet, interactionsPool);

        return subNet;
    }

    private void addToNetwork(Subnetwork subNet, List<RegulatoryInteraction> interactionsPool) {

        for (RegulatoryInteraction ri : interactionsPool) {
            if (subNet.attaches(ri)) {

                subNet.getRegulatoryInteractions().add(ri);
                if (!subNet.getProbeIds().contains(ri.geneA)) {
                    subNet.getProbeIds().add(ri.getGeneA());
                    addToNetwork(subNet, interactionsPool);
                }
                if (!subNet.getProbeIds().contains(ri.geneB)) {
                    subNet.getProbeIds().add(ri.getGeneB());
                    addToNetwork(subNet, interactionsPool);
                }
            }
        }
    }

    private List<Integer> getHivInteractants(File edgeFile) {
        BufferedReader reader = FileUtils.getBufferedReader(edgeFile);
        String line;
        List<Integer> hivInteractants = new ArrayList<Integer>();
        try {
            boolean first = true;
            while ((line = reader.readLine()) != null) {
                if (first) {
                    first = false;
                } else {
                    String[] split = line.split("\t");
                    if (split[5].equals("hivHuman")) {
                        int id = Integer.parseInt(split[0]);
                        if (!hivInteractants.contains(id)) {
                            hivInteractants.add(id);
                        }
                    }
                }
            }
            reader.close();
        } catch (IOException ex) {
            Logger.getLogger(ControllerStatistics.class.getName()).log(Level.SEVERE, null, ex);
        }
        return hivInteractants;
    }

    private List<Integer> getAllProbeIds(File edgeFile) {
        BufferedReader reader = FileUtils.getBufferedReader(edgeFile);
        String line;
        List<Integer> probeIds = new ArrayList<Integer>();
        try {
            boolean first = true;
            while ((line = reader.readLine()) != null) {
                if (first) {
                    first = false;
                } else {
                    String[] split = line.split("\t");
                    if (split[5].equals("hivHuman")) {
                        int id = Integer.parseInt(split[0]);
                        if (!probeIds.contains(id)) {
                            probeIds.add(id);
                        }
                    } else {
                        int id = Integer.parseInt(split[0]);
                        if (!probeIds.contains(id)) {
                            probeIds.add(id);
                        }
                        int id2 = Integer.parseInt(split[1]);
                        if (!probeIds.contains(id2)) {
                            probeIds.add(id2);
                        }
                    }

                }
            }
            reader.close();
        } catch (IOException ex) {
            Logger.getLogger(ControllerStatistics.class.getName()).log(Level.SEVERE, null, ex);
        }
        return probeIds;
    }

    private List<RegulatoryInteraction> getInteractionsFromFile(File edgeFile) {
        BufferedReader reader = FileUtils.getBufferedReader(edgeFile);
        String line;
        List<RegulatoryInteraction> regInts = new ArrayList<RegulatoryInteraction>();
        try {
            boolean first = true;
            while ((line = reader.readLine()) != null) {
                if (first) {
                    first = false;
                } else {

                    String[] split = line.split("\t");
//                    System.out.println(line);
                    if (!split[5].equals("hivHuman")) {
                        int id1 = Integer.parseInt(split[0]);
                        int id2 = Integer.parseInt(split[1]);
                        int to = Integer.parseInt(split[4]);
                        RegulatoryInteraction ri = new RegulatoryInteraction(id1, id2);
                        if (bayes.lastEntry().getKey() < to) {
                            to = bayes.lastEntry().getKey();
                        }
                        ri.setConfidence(bayes.get(to));
                        regInts.add(ri);
                    }
                }
            }
            reader.close();
        } catch (IOException ex) {
            Logger.getLogger(ControllerStatistics.class.getName()).log(Level.SEVERE, null, ex);
        }
        return regInts;
    }

    private Subnetwork rewire2(Subnetwork sn) {
        List<Integer> slots = sn.getSlots();
        Subnetwork rewired = new Subnetwork();
        rewired.getProbeIds().addAll(sn.getProbeIds());
        rewired.getRegulatoryInteractions().addAll(sn.getRegulatoryInteractions());
        int noOfSwitches = rewiresPerEdge * slots.size();
        for (int i = 0; i < noOfSwitches; i++) {
            makeSwitch(rewired);
        }
        return rewired;
    }

    private void makeSwitch(Subnetwork rewired) {
        List<RegulatoryInteraction> regInts = rewired.getRegulatoryInteractions();
        int randomIndex1 = twister.nextInt(regInts.size());
        int randomIndex2 = twister.nextInt(regInts.size());
        if (regInts.get(randomIndex1).getGeneA() == regInts.get(randomIndex2).getGeneB() || regInts.get(randomIndex2).getGeneA() == regInts.get(randomIndex1).getGeneB()) {
            makeSwitch(rewired);
        } else {
            RegulatoryInteraction ri_new1 = new RegulatoryInteraction(regInts.get(randomIndex1).getGeneA(), regInts.get(randomIndex2).getGeneB());
            RegulatoryInteraction ri_new2 = new RegulatoryInteraction(regInts.get(randomIndex2).getGeneA(), regInts.get(randomIndex1).getGeneB());
            if (twister.nextBoolean()) {
                ri_new1.setConfidence(regInts.get(randomIndex1).getConfidence());
                ri_new2.setConfidence(regInts.get(randomIndex2).getConfidence());
            } else {
                ri_new2.setConfidence(regInts.get(randomIndex1).getConfidence());
                ri_new1.setConfidence(regInts.get(randomIndex2).getConfidence());
            }
            regInts.set(randomIndex1, ri_new1);
            regInts.set(randomIndex2, ri_new2);

        }
    }

    private List<RegulatoryInteraction> getInteractionsPool(List<RegulatoryInteraction> allInteractions, boolean perturb) {
        List<RegulatoryInteraction> interactionsPool = new ArrayList<RegulatoryInteraction>();
        if (perturb) {
            for (RegulatoryInteraction ri : allInteractions) {
                double rand = twister.nextDouble();
                boolean add = true;
                if (rand >= ri.getConfidence()) {
                    add = false;
                }
                if (add) {
                    interactionsPool.add(ri);
                }
            }
        } else {
            interactionsPool.addAll(allInteractions);
        }
        return interactionsPool;
    }

    private class RegulatoryInteraction {

        private int geneA;
        private int geneB;
        private double confidence;

        public RegulatoryInteraction(int geneA, int geneB) {
            int[] ids = {geneA, geneB};
            Arrays.sort(ids);
            this.geneA = ids[0];
            this.geneB = ids[1];
        }

        public int getGeneA() {
            return geneA;
        }

        public void setGeneA(int geneA) {
            this.geneA = geneA;
        }

        public int getGeneB() {
            return geneB;
        }

        public void setGeneB(int geneB) {
            this.geneB = geneB;
        }

        public void setConfidence(double confidence) {
            this.confidence = confidence;
        }

        public double getConfidence() {
            return confidence;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final RegulatoryInteraction other = (RegulatoryInteraction) obj;
            if (this.geneA != other.geneA) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 23 * hash + this.geneA;
            hash = 23 * hash + this.geneB;
            return hash;
        }
    }

    private class Subnetwork {

        private List<RegulatoryInteraction> regulatoryInteractions = new ArrayList<RegulatoryInteraction>();
        private List<Integer> probeIds = new ArrayList<Integer>();

        boolean attaches(RegulatoryInteraction ri) {
            if (probeIds.contains(ri.geneA)) {
                if (!regulatoryInteractions.contains(ri)) {
                    return true;
                }
            }
            if (probeIds.contains(ri.geneB)) {
                if (!regulatoryInteractions.contains(ri)) {
                    return true;
                }
            }
            return false;
        }

        public List<Integer> getProbeIds() {
            return probeIds;
        }

        public List<RegulatoryInteraction> getRegulatoryInteractions() {
            return regulatoryInteractions;
        }

        public List<Integer> getSlots() {
            List<Integer> slots = new ArrayList<Integer>();
            for (RegulatoryInteraction ri : regulatoryInteractions) {
                slots.add(ri.getGeneA());
                slots.add(ri.getGeneB());
            }
            return slots;
        }
    }

    private List<Integer> getGeneIds(int probeId) {

        String sql = "select distinct gId from probeIdLookup where probeId=" + probeId;
        List<Integer> gIds = new ArrayList<Integer>();
        ResultSet rs = handle.runSQLquery(sql);
        try {
            while (rs.next()) {
                int gId = rs.getInt("gId");
                gIds.add(gId);
            }
            rs.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        if (gIds.size() > 1) {
            System.out.println("probe had more than one gene: " + probeId);
        }
        return gIds;
    }

    public String getGeneSymbolFromId(int id) {
        String sql = "select geneSymbol from interactions.geneInfo where geneId=" + id;
        ResultSet rs = handle.runSQLquery(sql);
        String s = null;
        try {
            if (rs.next()) {
                s = rs.getString("geneSymbol");
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        try {
            rs.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        return s;
    }
}
